﻿namespace GreenIsomer.Data.Entities.Transform.Gui {
	using System;
	using System.Windows.Forms;
	using System.IO;
	using System.Text;

	public partial class MainForm : Form {

		private bool updating;
		private Settings currentSettings;
		private string currentSettingsFilename;
		private bool currentSettingsDirty;
		private Output currentOutput;
		private ISsdlOutput currentSsdlOutput;
		private TypeConversion currentSsdlType;

		public MainForm() {
			InitializeComponent();
			CreateNew();
		}

		protected override void OnClosing(System.ComponentModel.CancelEventArgs e) {

			if(ConfirmSave() == DialogResult.Cancel) {
				e.Cancel = true;
				return;
			}

			base.OnClosing(e);
		}

		private void exitMenuItem_Click(object sender, EventArgs e) {
			Close();
		}

		private DialogResult ConfirmSave() {

			if(!currentSettingsDirty) return DialogResult.No;

			DialogResult result = MessageBox.Show(this, 
				"You currently have unsaved data.  Would you like to save your changes?", 
				"Confirm Unsaved Data", MessageBoxButtons.YesNoCancel, 
				MessageBoxIcon.Question, MessageBoxDefaultButton.Button3);

			if(result == DialogResult.Yes) {
				if(Save(false)) return DialogResult.Yes;
				return DialogResult.Cancel;
			}

			return result;
		}

		private void openMenuItem_Click(object sender, EventArgs e) {

			if(ConfirmSave() == DialogResult.Cancel) return;

			if(openFileDialog.ShowDialog(this) != DialogResult.OK) return;

			SetStatus("Loading...");

			try {
				currentSettings = Settings.Load(openFileDialog.FileName);
			} catch(Exception ex) {
				DisplayError(ex);
				return;
			}

			if(currentSettings.Input == null)
				currentSettings.Input = new EdmxInput(); //Only one supported curretnly

			currentSettingsFilename = openFileDialog.FileName;
			currentSettingsDirty = false;

			UpdateAll();

			ClearStatus();
		}

		private void DisplayError(Exception ex) {
			ClearStatus();

			MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
		}

		private void newMenuItem_Click(object sender, EventArgs e) {

			if(ConfirmSave() == DialogResult.Cancel) return;

			CreateNew();
		}

		private void CreateNew() {
			currentSettings = new Settings();
			currentSettings.Input = new EdmxInput(); //Only one supported curretnly
			currentSettingsFilename = null;
			currentSettingsDirty = false;

			UpdateAll();
		}

		private void UpdateAll() {
			UpdateTitle();
			UpdateInput();
			UpdateOutput();
			UpdateCurrentOutput();
			UpdateTypes();
			UpdateCurrentType();
		}

		private void UpdateTypes() {
			updating = true;

			ssdlTypesListBox.Items.Clear();
			if(currentSsdlOutput != null)
				foreach(TypeConversion type in currentSsdlOutput.TypeConversions)
					ssdlTypesListBox.Items.Add(type.ToString());

			if(ssdlTypesListBox.Items.Count == 0) {
				ssdlTypeRemoveButton.Enabled = false;
				ssdlTypesListBox.TabStop = false;
			} else {
				ssdlTypeRemoveButton.Enabled = true;
				ssdlTypesListBox.TabStop = true;
				ssdlTypesListBox.SelectedIndex = 0;
			}
			
			updating = false;
		}

		private void UpdateInput() {
			updating = true;

			inputFileTextBox.Text = currentSettings.Input.File;

			updating = false;
		}

		private void UpdateOutput() {
			updating = true;

			outputListBox.Items.Clear();
			foreach(Output output in currentSettings.Output)
				outputListBox.Items.Add(output.ToString());

			if(outputListBox.Items.Count == 0) {
				removeOutputMenuItem.Enabled = false;
				outputListBox.TabStop = false;
			} else {
				removeOutputMenuItem.Enabled = true;
				outputListBox.TabStop = true;
				outputListBox.SelectedIndex = 0;
			}

			updating = false;
		}

		private void UpdateTitle() {
			string filename = string.IsNullOrEmpty(currentSettingsFilename) ? "Untitled" : currentSettingsFilename;
			Text = filename + " - GIDEX for Windows";
		}

		private void UpdateCurrentType() {
			updating = true;

			currentSsdlType = ssdlTypesListBox.SelectedIndex < 0
				? null : currentSsdlOutput.TypeConversions[ssdlTypesListBox.SelectedIndex];

			if(currentSsdlType == null) ssdlTypePanel.Visible = false;
			else {

				ssdlTypeInputTextBox.Text = currentSsdlType.InputType;
				ssdlTypeOutputTextBox.Text = currentSsdlType.OutputType;

				ssdlTypePanel.Visible = true;
			}

			updating = false;
		}

		private void UpdateCurrentOutput() {
			updating = true;

			currentOutput = outputListBox.SelectedIndex < 0 
				? null : currentSettings.Output[outputListBox.SelectedIndex];
			currentSsdlOutput = currentOutput as ISsdlOutput;

			if(currentOutput == null) outputPanel.Visible = false;
			else {
				specificOutputPanel.Text = string.Format("{0} Output Settings:", currentOutput.TypeDescription);

				outputFileDialog.DefaultExt = currentOutput.DefaultFileExtension;
				outputFileDialog.Filter = string.Format("{0} (*.{1})|*.{1}|All Files (*.*)|*.*",
					currentOutput.TypeDescription,
					currentOutput.DefaultFileExtension);

				outputFileTextBox.Text = currentOutput.File;

				outputPanel.Visible = true;
			}

			if(currentSsdlOutput == null) ssdlOutputPanel.Visible = false;
			else {

				SetNullableStringValue(currentSsdlOutput.Provider, ssdlProviderCheckBox, ssdlProviderTextBox);
				SetNullableStringValue(currentSsdlOutput.ProviderManifestToken, ssdlProviderManifestTokenCheckBox, ssdlProviderManifestTokenTextBox);
				SetNullableStringValue(currentSsdlOutput.Schema, ssdlSchemaCheckBox, ssdlSchemaTextBox);

				ssdlOutputPanel.Dock = DockStyle.Fill;
				ssdlOutputPanel.Visible = true;
			}

			updating = false;
		}

		private void ClearStatus() {
			Cursor = Cursors.Default;
			statusLabel.Text = string.Empty;
		}

		private void SetStatus(string statusText) {
			statusLabel.Text = statusText;
			Cursor = Cursors.WaitCursor;
		}

		private void saveAsMenuItem_Click(object sender, EventArgs e) {
			Save(true);
		}

		private void saveMenuItem_Click(object sender, EventArgs e) {
			Save(false);
		}

		private bool Save(bool forcePrompt) {

			bool prompt = forcePrompt || string.IsNullOrEmpty(currentSettingsFilename);
			string filename;

			if(prompt) {
				saveFileDialog.FileName = currentSettingsFilename;
				if(saveFileDialog.ShowDialog(this) == DialogResult.Cancel)
					return false;
				filename = saveFileDialog.FileName;
			} else
				filename = currentSettingsFilename;

			SetStatus("Saving...");

			try {
				currentSettings.Save(filename);
			} catch(Exception ex) {
				DisplayError(ex);
				return false;
			}
			currentSettingsFilename = filename;
			currentSettingsDirty = false;

			UpdateTitle();

			ClearStatus();

			return true;
		}

		private void inputFileTextBox_TextChanged(object sender, EventArgs e) {
			string text = inputFileTextBox.Text;

			inputFileMakeRelativeButton.Enabled = !string.IsNullOrEmpty(text) && !text.Trim().StartsWith(".");

			if(updating) return;

			currentSettings.Input.File = text;
			currentSettingsDirty = true;
		}

		private void outputListBox_SelectedIndexChanged(object sender, EventArgs e) {
			if(updating) return;
			UpdateCurrentOutput();
			UpdateTypes();
			UpdateCurrentType();
		}

		private void addSsdlMenuItem_Click(object sender, EventArgs e) {

			currentSettings.Output.Add(new SsdlOutput());
			UpdateOutput();

			updating = true;
			outputListBox.SelectedIndex = outputListBox.Items.Count - 1;
			updating = false;

			UpdateCurrentOutput();
			UpdateTypes();
			UpdateCurrentType();

			currentSettingsDirty = true;

			outputFileTextBox.Focus();
		}

		private void addCsdlMenuItem_Click(object sender, EventArgs e) {

			currentSettings.Output.Add(new CsdlOutput());
			UpdateOutput();

			updating = true;
			outputListBox.SelectedIndex = outputListBox.Items.Count - 1;
			updating = false;

			UpdateCurrentOutput();
			UpdateTypes();
			UpdateCurrentType();

			currentSettingsDirty = true;

			outputFileTextBox.Focus();
		}

		private void addMslMenuItem_Click(object sender, EventArgs e) {

			currentSettings.Output.Add(new MslOutput());
			UpdateOutput();

			updating = true;
			outputListBox.SelectedIndex = outputListBox.Items.Count - 1;
			updating = false;

			UpdateCurrentOutput();
			UpdateTypes();
			UpdateCurrentType();

			currentSettingsDirty = true;

			outputFileTextBox.Focus();
		}

		private void removeOutputMenuItem_Click(object sender, EventArgs e) {
			int index = outputListBox.SelectedIndex;
			if(index < 0) return;

			currentSettings.Output.RemoveAt(index);
			UpdateOutput();

			if(outputListBox.Items.Count != 0) {
				updating = true;
				if(index < outputListBox.Items.Count) outputListBox.SelectedIndex = index;
				else outputListBox.SelectedIndex = outputListBox.Items.Count - 1;
				updating = false;

				outputListBox.Focus();
			} else inputFileTextBox.Focus();
			UpdateCurrentOutput();
			UpdateTypes();
			UpdateCurrentType();

			currentSettingsDirty = true;
		}

		private void outputFileTextBox_TextChanged(object sender, EventArgs e) {
			string text = outputFileTextBox.Text;

			outputFileMakeRelativeButton.Enabled = !string.IsNullOrEmpty(text) && !text.Trim().StartsWith(".");

			if(updating) return;

			currentOutput.File = text;
			currentSettingsDirty = true;
		}

		private void outputFileTextBox_Leave(object sender, EventArgs e) {
			updating = true;
			outputListBox.Items[outputListBox.SelectedIndex] = currentOutput.ToString();
			updating = false;
		}

		private void ssdlProviderTextBox_TextChanged(object sender, EventArgs e) {
			if(updating) return;
			currentSsdlOutput.Provider = ssdlProviderTextBox.Text;
			currentSettingsDirty = true;
		}

		private void ssdlProviderManifestTokenTextBox_TextChanged(object sender, EventArgs e) {
			if(updating) return;
			currentSsdlOutput.ProviderManifestToken = ssdlProviderManifestTokenTextBox.Text;
			currentSettingsDirty = true;
		}

		private void ssdlSchemaTextBox_TextChanged(object sender, EventArgs e) {
			if(updating) return;
			currentSsdlOutput.Schema = ssdlSchemaTextBox.Text;
			currentSettingsDirty = true;
		}

		private void ssdlProviderCheckBox_CheckedChanged(object sender, EventArgs e) {
			if(updating) return;
			updating = true;
			currentSsdlOutput.Provider = GetNullableStringValue(ssdlProviderCheckBox, ssdlProviderTextBox);
			updating = false;
			currentSettingsDirty = true;
		}

		private void ssdlProviderManifestTokenCheckBox_CheckedChanged(object sender, EventArgs e) {
			if(updating) return;
			updating = true;
			currentSsdlOutput.ProviderManifestToken = GetNullableStringValue(ssdlProviderManifestTokenCheckBox, ssdlProviderManifestTokenTextBox);
			updating = false;
			currentSettingsDirty = true;
		}

		private void ssdlSchemaCheckBox_CheckedChanged(object sender, EventArgs e) {
			if(updating) return;
			updating = true;
			currentSsdlOutput.Schema = GetNullableStringValue(ssdlSchemaCheckBox, ssdlSchemaTextBox);
			updating = false;
			currentSettingsDirty = true;
		}

		private string GetNullableStringValue(CheckBox checkBox, TextBox textBox) {
			if(checkBox.Checked) {
				if(textBox.ReadOnly) {
					textBox.ReadOnly = false;
					textBox.TabStop = true;
					textBox.Text = textBox.Tag as string;
					textBox.Focus();
				}
				return textBox.Text;
			} else {
				if(!textBox.ReadOnly) {
					textBox.ReadOnly = true;
					textBox.TabStop = false;
					textBox.Tag = textBox.Text;
					textBox.Text = "No Modification";
				}
				return null;
			}
		}

		private void SetNullableStringValue(string value, CheckBox checkBox, TextBox textBox) {
			textBox.Tag = string.Empty;
			if(value == null) {
				checkBox.Checked = false;
				textBox.ReadOnly = true;
				textBox.TabStop = false;
				textBox.Text = "No Modification";
			} else {
				checkBox.Checked = true;
				textBox.ReadOnly = false;
				textBox.TabStop = true;
				textBox.Text = value;
			}
		}

		private void nullableStringCheckBox_Enter(object sender, EventArgs e) {
			((Control)sender).Parent.BackColor = System.Drawing.SystemColors.Highlight;
		}

		private void nullableStringCheckBox_Leave(object sender, EventArgs e) {
			((Control)sender).Parent.BackColor = System.Drawing.SystemColors.ButtonFace;
		}

		private void inputFileSelectButton_Click(object sender, EventArgs e) {

			string filename = inputFileTextBox.Text ?? string.Empty;
			if(!string.IsNullOrEmpty(currentSettingsFilename) && filename.StartsWith("."))
				filename = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(currentSettingsFilename), filename));
			inputFileDialog.FileName = filename;

			if(inputFileDialog.ShowDialog(this) == DialogResult.OK) {
				inputFileTextBox.Text = inputFileDialog.FileName;
				inputFileTextBox.Focus();
			}

		}

		private void inputFileMakeRelativeButton_Click(object sender, EventArgs e) {
			MakeFileRelative(inputFileTextBox);
			inputFileTextBox.Focus();
		}

		private void MakeFileRelative(TextBox textBox) {

			string text = textBox.Text.Trim();
			if(string.IsNullOrEmpty(text) || text.StartsWith(".")) return;

			if(string.IsNullOrEmpty(currentSettingsFilename)) {
				MessageBox.Show(this, "Your configuration file must be saved before completing this operation.",
					"Save Required", MessageBoxButtons.OK, MessageBoxIcon.Information);
				if(!Save(true)) return;
			}

			string[] currentPathParts = Path.GetDirectoryName(currentSettingsFilename).ToUpperInvariant()
				.Trim(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar);
			string[] targetPathParts = Path.GetDirectoryName(text).ToUpperInvariant()
				.Trim(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar);

			int equalLevels = 0;
			int maxParts = Math.Min(currentPathParts.Length, targetPathParts.Length);
			for (int i = 0; i < maxParts; i++) {
				if (currentPathParts[i] != targetPathParts[i]) break;
				equalLevels++;
			}

			if(equalLevels == 0) {
				MessageBox.Show(this, "The file appears to be on a different drive than the configuration file.  A relative path could not be set.",
					"Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return;
			}

			StringBuilder relativePath = new StringBuilder();

			for(int i = equalLevels; i < currentPathParts.Length; i++) {
				if (i != equalLevels) relativePath.Append(Path.DirectorySeparatorChar);
				relativePath.Append("..");
			}

			if (relativePath.Length == 0) relativePath.Append(".");

			for(int i = equalLevels; i < targetPathParts.Length; i++) {
				relativePath.Append(Path.DirectorySeparatorChar);
				relativePath.Append(targetPathParts[i]);
			}

			relativePath.Append(Path.DirectorySeparatorChar);
			relativePath.Append(Path.GetFileName(text));

			textBox.Text = relativePath.ToString();

		}

		private void outputFileSelectButton_Click(object sender, EventArgs e) {

			string filename = outputFileTextBox.Text ?? string.Empty;
			if(!string.IsNullOrEmpty(currentSettingsFilename) && filename.StartsWith("."))
				filename = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(currentSettingsFilename), filename));
			outputFileDialog.FileName = filename;

			if(outputFileDialog.ShowDialog(this) == DialogResult.OK) {
				outputFileTextBox.Text = outputFileDialog.FileName;
				outputFileTextBox.Focus();
			}

		}

		private void outputFileMakeRelativeButton_Click(object sender, EventArgs e) {
			MakeFileRelative(outputFileTextBox);
			outputFileTextBox.Focus();
		}

		private void ssdlTypesListBox_SelectedIndexChanged(object sender, EventArgs e) {
			if(updating) return;
			UpdateCurrentType();
		}

		private void ssdlTypeAddButton_Click(object sender, EventArgs e) {

			currentSsdlOutput.TypeConversions.Add(new TypeConversion());
			UpdateTypes();

			updating = true;
			ssdlTypesListBox.SelectedIndex = ssdlTypesListBox.Items.Count - 1;
			updating = false;

			UpdateCurrentType();

			currentSettingsDirty = true;

			ssdlTypeInputTextBox.Focus();
		}

		private void ssdlTypeRemoveButton_Click(object sender, EventArgs e) {
			int index = ssdlTypesListBox.SelectedIndex;
			if(index < 0) return;

			currentSsdlOutput.TypeConversions.RemoveAt(index);
			UpdateTypes();

			if(ssdlTypesListBox.Items.Count != 0) {
				updating = true;
				if(index < ssdlTypesListBox.Items.Count) ssdlTypesListBox.SelectedIndex = index;
				else ssdlTypesListBox.SelectedIndex = ssdlTypesListBox.Items.Count - 1;
				updating = false;

				ssdlTypesListBox.Focus();
			} else ssdlTypeAddButton.Focus();
			UpdateCurrentType();

			currentSettingsDirty = true;
		}

		private void ssdlTypeInputTextBox_TextChanged(object sender, EventArgs e) {
			if(updating) return;
			currentSsdlType.InputType = ssdlTypeInputTextBox.Text;
			currentSettingsDirty = true;
		}

		private void ssdlTypeOutputTextBox_TextChanged(object sender, EventArgs e) {
			if(updating) return;
			currentSsdlType.OutputType = ssdlTypeOutputTextBox.Text;
			currentSettingsDirty = true;
		}

		private void ssdlTypeTextBox_Leave(object sender, EventArgs e) {
			updating = true;
			ssdlTypesListBox.Items[ssdlTypesListBox.SelectedIndex] = currentSsdlType.ToString();
			updating = false;
		}

		private void addEdmxMenuItem_Click(object sender, EventArgs e) {

			currentSettings.Output.Add(new EdmxOutput());
			UpdateOutput();

			updating = true;
			outputListBox.SelectedIndex = outputListBox.Items.Count - 1;
			updating = false;

			UpdateCurrentOutput();
			UpdateTypes();
			UpdateCurrentType();

			currentSettingsDirty = true;

			outputFileTextBox.Focus();
		}

	}

}